package com.example.myyunpan.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.myyunpan.component.RedisComponent;
import com.example.myyunpan.entity.config.AppConfig;
import com.example.myyunpan.entity.constants.Constants;
import com.example.myyunpan.entity.domain.FileInfo;
import com.example.myyunpan.entity.domain.UserInfo;
import com.example.myyunpan.entity.dto.DownloadFileDto;
import com.example.myyunpan.entity.dto.SessionWebUserDto;
import com.example.myyunpan.entity.dto.UploadResultDto;
import com.example.myyunpan.entity.dto.UserSpaceDto;
import com.example.myyunpan.entity.enums.*;
import com.example.myyunpan.entity.query.FileInfoQuery;
import com.example.myyunpan.entity.query.SimplePage;
import com.example.myyunpan.entity.vo.FolderVO;
import com.example.myyunpan.entity.vo.PaginationResultVO;
import com.example.myyunpan.exception.BusinessException;
import com.example.myyunpan.mapper.FileInfoMapper;
import com.example.myyunpan.mapper.UserInfoMapper;
import com.example.myyunpan.service.FileInfoService;
import com.example.myyunpan.utils.CopyTools;
import com.example.myyunpan.utils.DateUtil;
import com.example.myyunpan.utils.MyFileUtils;
import com.example.myyunpan.utils.StringTools;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.apache.juli.FileHandler.DEFAULT_BUFFER_SIZE;

/**
* @author 111
* @description 针对表【file_info(文件信息)】的数据库操作Service实现
* @createDate 2024-10-23 11:41:27
*/
@Service
public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo>
implements FileInfoService {

    private static final Logger logger = LoggerFactory.getLogger(FileInfoServiceImpl.class);

    @Autowired
    public FileInfoMapper fileInfoMapper;

    @Autowired
    private RedisComponent redisComponent;

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private AppConfig appConfig;

    @Autowired
    private MyFileUtils myFileUtils;

    @Override
    public PaginationResultVO queryPageInfo(FileInfoQuery query) {
        int count = fileInfoMapper.selectCount(query);
        int pageSize = query.getPageSize() == null ? PageSize.SIZE15.getSize() : query.getPageSize();
        SimplePage simplePage = new SimplePage(query.getPageNo(), count, pageSize);
        query.setSimplePage(simplePage);
        List<FileInfo> list=fileInfoMapper.selectList(query);
        PaginationResultVO<FileInfo> result= new PaginationResultVO(count, simplePage.getPageSize(), simplePage.getPageNo(), simplePage.getPageTotal(), list);
        return result;
    }

    /**
     * 文件上传，上传已经逻辑删除的文件，
     * 判断是否文件md5值是否存在，
     * 如果存在，就直接插入新的文件记录，
     * 但MD5值相同并且文件路径指向同一个文件
     * @param webUserDto
     * @param fileId
     * @param file
     * @param fileName
     * @param filePid
     * @param fileMd5
     * @param chunkIndex
     * @param chunks
     * @return
     */
    @Override
    @Transactional
    public UploadResultDto uploadFile(SessionWebUserDto webUserDto, String fileId, MultipartFile file, String fileName, String filePid, String fileMd5, Integer chunkIndex, Integer chunks) {

        File tempFolder = null;

        Boolean uploadSuccess = true;
        try{

        UploadResultDto resultDto = new UploadResultDto();
//        获取fileid
        if(StringTools.isEmpty(fileId)){
            fileId = StringTools.getRandomNumber(Constants.LENGTH_10);
        }
        resultDto.setFileId(fileId);
        Date curdate = new Date();
//        检查空间是否足够
        UserSpaceDto userSpaceDto = (UserSpaceDto) redisComponent.getUserSpaceUser(webUserDto.getUserId());

//        第一个文件分片，通过MD5 判断是否已存在，如果存在就直接秒传
        if(chunkIndex == 0){
//            查询是否存在相同的fileMd5的文件
            FileInfoQuery fileInfoQuery = new FileInfoQuery();
            fileInfoQuery.setFileMd5(fileMd5);
            fileInfoQuery.setSimplePage(new SimplePage(0,1));
            fileInfoQuery.setStatus(FileStatusEnums.USING.getStatus());
            List<FileInfo> list = fileInfoMapper.selectList(fileInfoQuery);
            /**
             * 存在就秒传,在数据库中根据md5查询是否存在已经上传的文件，
             * 如果存在就只需要对数据库进行新的数据插入，因为文件已经存在，所以不需要上传文件
             * 只需要指向文件的指针，不需要上传文件
             */
            if(!list.isEmpty()){
                FileInfo dbFileInfo =list.get(0);
                if(dbFileInfo.getFileSize() + userSpaceDto.getUseSpace() > userSpaceDto.getTotalSpace()){
                    throw new BusinessException(ResponseCodeEnum.CODE_904);
                }
                dbFileInfo.setFileId(fileId);
                dbFileInfo.setFilePid(filePid);
                dbFileInfo.setUserId(webUserDto.getUserId());
                dbFileInfo.setFileMd5(null);
                dbFileInfo.setCreateTime(curdate);
                dbFileInfo.setLastUpdateTime(curdate);
                dbFileInfo.setStatus(FileStatusEnums.USING.getStatus());
                dbFileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());
                dbFileInfo.setFileMd5(fileMd5);
//                文件重命名
                fileName = autoRename(filePid, webUserDto.getUserId(), fileName);
                dbFileInfo.setFileName(fileName);
                fileInfoMapper.insert(dbFileInfo);
                resultDto.setStatus(UploadStatusEnums.UPLOAD_SECONDS.getCode());
//                更新用户使用空间，更新数据库
                updateUserSpace(webUserDto, dbFileInfo.getFileSize());
                return resultDto;
            }
        }
            /**
             * 文件不存在，正常上传，使用分片上传
             */
//          判断磁盘的空间
            Long currentTempSize = redisComponent.getFileTempSize(webUserDto.getUserId(), fileId);
            if(file.getSize() + currentTempSize + userSpaceDto.getUseSpace() > userSpaceDto.getTotalSpace()){
                throw new BusinessException(ResponseCodeEnum.CODE_904);
            }
//          创建上传分片文件的临时目录
            String tempFolderName = appConfig.getProjectFolder() + Constants.FILE_FOLDER_TEMP;
            String currentUserFolderName =webUserDto.getUserId()+ fileId;
//            判断文件是否存在，并创建
            tempFolder=new File(tempFolderName + currentUserFolderName);
            if(!tempFolder.exists()){
                tempFolder.mkdirs();
            }
//            创建分片文件
            File newfile = new File(tempFolder.getPath() + "/" + chunkIndex);
            file.transferTo(newfile);
            if(chunkIndex < chunks - 1){
                resultDto.setStatus(UploadStatusEnums.UPLOADING.getCode());
                redisComponent.saveFileTempSize(webUserDto.getUserId(), fileId, file.getSize());
                return resultDto;
            }
            /**
             * 最后一个分片上传完成之后，进行数据库文件数据的更新，并且将分片合并
             */
            String month = DateUtil.format(new Date(), DateTimePatternEnum.YYYYMM.getPattern());
            String fileSuffix = StringTools.getFileSuffix(fileName);
            String realFileName = currentUserFolderName + fileSuffix;
            FileTypeEnums fileTypeBySuffix = FileTypeEnums.getFileTypeBySuffix(fileSuffix);
//            自动重命名
            fileName = autoRename(filePid, webUserDto.getUserId(), fileName);

            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileId(fileId);
            fileInfo.setUserId(webUserDto.getUserId());
            fileInfo.setFileMd5(fileMd5);
            fileInfo.setFileName(fileName);
            fileInfo.setFilePath(month + "/" + realFileName);
            fileInfo.setFilePid(filePid);
            fileInfo.setCreateTime(curdate);
            fileInfo.setLastUpdateTime(curdate);
            fileInfo.setFileCategory(fileTypeBySuffix.getCategory().getCategory());
            fileInfo.setFileType(fileTypeBySuffix.getType());
            fileInfo.setStatus(FileStatusEnums.TRANSFER.getStatus());
            fileInfo.setFolderType(FileFolderTypeEnums.FILE.getType());
            fileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());
            fileInfoMapper.insert(fileInfo);
            /*
            * TODO totalSize为分片文件大小总和
            * */
            Long totalSize = redisComponent.getFileTempSize(webUserDto.getUserId(), fileId);
            updateUserSpace(webUserDto, totalSize);

            resultDto.setStatus(UploadStatusEnums.UPLOAD_FINISH.getCode());
            /**
             * 事物提交之后进行的操作
             */
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    myFileUtils.transferFile(fileInfo.getFileId(),webUserDto);
                }
            });
            return resultDto;
        } catch (BusinessException e) {
            uploadSuccess = false;
            logger.error("文件上传失败", e);
            throw e;
        } catch (Exception e) {
            uploadSuccess = false;
            logger.error("文件上传失败", e);
            throw new BusinessException("文件上传失败");
        }
    }

    /**
     * 获取预览文件
     * @param response
     * @param fileId
     * @param userId
     */
    @Override
    public void getFile(HttpServletResponse response, String fileId, String userId) {
        String filePath=null;
        FileInfo fileInfo = null;
        if(fileId.endsWith(".ts")){
            String[] tsArr = fileId.split("_");
            String realFileId = tsArr[tsArr.length - 2];
            fileInfo = fileInfoMapper.selectByFileIdAndUserIdFileInfo(realFileId, userId);
            String fileName = fileInfo.getFilePath();
            fileName = StringTools.getFileNameNoSuffix(fileName)+ "/" + fileId;
            filePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER_FILE + "/" + fileName;
        }else {
            fileInfo = fileInfoMapper.selectByFileIdAndUserIdFileInfo(fileId, userId);
            if(fileInfo == null){
                return;
            }
            if(FileCategoryEnums.VIDEO.getCategory().equals(fileInfo.getFileCategory())){
                String fileNameNoSuffix = StringTools.getFileNameNoSuffix(fileInfo.getFilePath());
                filePath = appConfig.getProjectFolder() +
                        Constants.FILE_FOLDER_FILE + "/" +
                        fileNameNoSuffix + "/" +
                        Constants.M3U8_NAME;

            }else {
                filePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER_FILE + fileInfo.getFilePath();
            }
        }
        File file=new File(filePath);
        if(!file.exists()){
            return;
        }
        // 设置响应头
        response.reset();
        response.setBufferSize(DEFAULT_BUFFER_SIZE);
        if (fileId.endsWith(".ts")) {
            // 设置视频切片文件的响应头
            response.setContentType("video/mp2t"); // 设置内容类型为视频切片
            response.setHeader("Content-Length", String.valueOf(file.length()));
            response.setHeader("Content-Disposition", "inline; filename=\"" + file.getName() + "\"");
        } else if (FileCategoryEnums.VIDEO.getCategory().equals(fileInfo.getFileCategory())) {
            // 设置 M3U8 文件的响应头
            response.setContentType("application/vnd.apple.mpegurl"); // 设置内容类型为 M3U8
            response.setHeader("Content-Length", String.valueOf(file.length()));
            response.setHeader("Content-Disposition", "inline; filename=\"" + file.getName() + "\"");
        } else {
            // 设置普通文件的响应头
            response.setContentType("application/octet-stream"); // 设置内容类型为二进制流
            response.setHeader("Content-Length", String.valueOf(file.length()));
            response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
        }
        myFileUtils.readFile(response, filePath);

    }

    /**
     * 创建文件目录
     * @param filePid
     * @param userId
     * @param fileName
     * @return
     */
    @Override
    public FileInfo newFolder(String filePid, String userId, String fileName) {
        checkFileName(filePid, userId, fileName, FileFolderTypeEnums.FOLDER.getType());
        Date curdate = new Date();
        FileInfo fileInfo = new FileInfo();
        fileInfo.setFileId(StringTools.getRandomString(Constants.LENGTH_10));
        fileInfo.setUserId(userId);
        fileInfo.setFilePid(filePid);
        fileInfo.setFileName(fileName);
        fileInfo.setFolderType(FileFolderTypeEnums.FOLDER.getType());
        fileInfo.setCreateTime(curdate);
        fileInfo.setLastUpdateTime(curdate);
        fileInfo.setStatus(FileStatusEnums.USING.getStatus());
        fileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());
        fileInfoMapper.insert(fileInfo);
        return fileInfo;
    }

    @Override
    public List<FolderVO> getFolderInfo(String path, String userId) {
        String[] patharr= path.split("/");
        FileInfoQuery query = new FileInfoQuery();
        query.setUserId(userId);
        query.setFolderType(FileFolderTypeEnums.FOLDER.getType());
        query.setFileIdArray(patharr);
        String orderBy = "field(file_id,\"" + StringUtils.join(patharr, "\",\"") + "\")";
        query.setOrderBy(orderBy);
        List<FileInfo> fileInfos = fileInfoMapper.selectList(query);
        return CopyTools.copyList(fileInfos, FolderVO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileInfo rename(String userId, String fileId, String fileName) {
        FileInfo fileInfo = fileInfoMapper.selectByFileIdAndUserIdFileInfo(fileId, userId);
        if(fileInfo == null){
            throw new BusinessException("文件不存在");
        }
        if (fileInfo.getFileName().equals(fileName)) {
            return fileInfo;
        }
        String filePid = fileInfo.getFilePid();
        checkFileName(filePid, userId, fileName, fileInfo.getFolderType());
//            获取文件后缀
        if(FileFolderTypeEnums.FILE.getType().equals(fileInfo.getFolderType())){
            fileName= fileName + "." + StringTools.getFileSuffix(fileInfo.getFileName());
        }
        Date curdate = new Date();
        FileInfo fileInfodb = new FileInfo();
        fileInfodb.setFileName(fileName);
        fileInfodb.setLastUpdateTime(curdate);
        fileInfoMapper.updateByFileIdAndUserIdFileInfo(fileInfodb, fileId, userId);
        FileInfoQuery fileInfoQuery = new FileInfoQuery();
        fileInfoQuery.setFilePid(filePid);
        fileInfoQuery.setUserId(userId);
        fileInfoQuery.setFileName(fileName);
        fileInfoQuery.setDelFlag(FileDelFlagEnums.USING.getFlag());
        Integer count = this.fileInfoMapper.selectCount(fileInfoQuery);
        if (count > 1) {
            throw new BusinessException("文件名" + fileName + "已经存在");
        }
        fileInfo.setFileName(fileName);
        fileInfo.setLastUpdateTime(curdate);
        return fileInfo;

    }

    @Override
    public List<FileInfo> findListByParam(FileInfoQuery fileInfoQuery) {
        List<FileInfo> fileInfos = fileInfoMapper.selectList(fileInfoQuery);
        return fileInfos;
    }

    /**
     * 参数校验：
     * 检查 fileIds 和 filePid 是否相同，如果相同则抛出异常。
     * 检查 filePid 是否为 "0"，如果不是，则查询目标文件夹是否存在且状态为 "使用中"，如果不存在或状态不正确则抛出异常。
     * 文件ID解析：
     * 将 fileIds 按逗号分割成数组。
     * 查询目标文件夹中的文件：
     * 构建查询条件，查询目标文件夹中的所有文件，并将其文件名映射到一个 Map 中。
     * 查询选中的文件：
     * 构建查询条件，查询需要移动的文件。
     * 重命名并更新文件：
     * 遍历选中的文件，检查文件名是否已存在于目标文件夹中，如果存在则重命名。
     * 更新文件的父文件夹ID和文件名
     * @param userId
     * @param fileIds
     * @param filePid
     */
    @Override
    public void changeFileFolder(String userId, String fileIds, String filePid) {
        if (fileIds.equals(filePid)) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        if (!Constants.ZERO_STR.equals(filePid)) {
            FileInfo fileInfo = fileInfoMapper.selectByFileIdAndUserIdFileInfo(filePid, userId);
            if (fileInfo == null || !FileDelFlagEnums.USING.getFlag().equals(fileInfo.getDelFlag())) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
        }
        String[] fileIdArray = fileIds.split(",");

        FileInfoQuery query = new FileInfoQuery();
        query.setFilePid(filePid);
        query.setUserId(userId);
        List<FileInfo> dbFileList = fileInfoMapper.selectList(query);

        Map<String, FileInfo> dbFileNameMap = dbFileList.stream().collect(Collectors.toMap(FileInfo::getFileName, Function.identity(), (file1, file2) -> file2));
        //查询选中的文件
        query = new FileInfoQuery();
        query.setUserId(userId);
        query.setFileIdArray(fileIdArray);
        List<FileInfo> selectFileList = fileInfoMapper.selectList(query);

        //将所选文件重命名
        for (FileInfo item : selectFileList) {
            FileInfo rootFileInfo = dbFileNameMap.get(item.getFileName());
            //文件名已经存在，重命名被还原的文件名
            FileInfo updateInfo = new FileInfo();
            if (rootFileInfo != null) {
                String fileName = StringTools.rename(item.getFileName());
                updateInfo.setFileName(fileName);
            }
            updateInfo.setFilePid(filePid);
            fileInfoMapper.updateByFileIdAndUserIdFileInfo(updateInfo, item.getFileId(), userId);
        }
    }

    /*
    * 创建下载地址连接，返回code
    * */
    @Override
    public String createDownloadUrl(String fileId, String userId) {
        FileInfo fileInfo = fileInfoMapper.selectByFileIdAndUserIdFileInfo(fileId, userId);
        if(fileInfo == null){
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        if(FileFolderTypeEnums.FOLDER.getType().equals(fileInfo.getFolderType())){
            throw new BusinessException("请选择文件");
        }
        String code = StringTools.getRandomString(Constants.LENGTH_50);

        DownloadFileDto downloadFileDto = new DownloadFileDto();
        downloadFileDto.setDownloadCode(code);
        downloadFileDto.setFilePath(fileInfo.getFilePath());
        downloadFileDto.setFileName(fileInfo.getFileName());
        /*
        * 以code为key，将downloadFileDto对象存入redis中，过期时间为1天，key为code，value为downloadFileDto对象
        * */
        redisComponent.saveDownloadCode(code,downloadFileDto);
        return code;
    }

    /**
     * 下载文件
     * @param request
     * @param response
     * @param code
     */
    @Override
    public void download(HttpServletRequest request, HttpServletResponse response, String code) {
        DownloadFileDto downloadFileDto=redisComponent.getDownloadCode(code);
        if(downloadFileDto==null){
            return;
        }
        String filePath =appConfig.getProjectFolder()+Constants.FILE_FOLDER_FILE+ downloadFileDto.getFilePath();
        String fileName = downloadFileDto.getFileName();
        try {
        response.setContentType("application/x-msdownload; charset=UTF-8");
        if (request.getHeader("User-Agent").toLowerCase().indexOf("msie") > 0) {//IE浏览器
            fileName = URLEncoder.encode(fileName, "UTF-8");
        } else {
            fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1");
        }
        response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");
        myFileUtils.readFile(response, filePath);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    /**
     * 回收删除
     * @param userId
     * @param fileIds
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeFile4RecycleBatch(String userId, String fileIds) {
        String[] fileIdArray = fileIds.split(",");
        FileInfoQuery fileInfoQuery = new FileInfoQuery();
        fileInfoQuery.setUserId(userId);
        fileInfoQuery.setFileIdArray(fileIdArray);
        fileInfoQuery.setDelFlag(FileDelFlagEnums.USING.getFlag());
        List<FileInfo> fileInfos = fileInfoMapper.selectList(fileInfoQuery);
        if(fileInfos.isEmpty()){
            return;
        }
//        将文件夹下所以的文件全部查出来，放在列表中，方便后面删除
        List<String> delFilePidList = new ArrayList<>();
        for (FileInfo fileInfo : fileInfos) {
            findAllSubFolderFileList(fileInfo.getFileId(),userId,delFilePidList,FileDelFlagEnums.USING.getFlag());
        }
//        将目录下所有文件更新为已经删除状态
        if(!delFilePidList.isEmpty()){
            FileInfo updateInfo = new FileInfo();
            updateInfo.setDelFlag(FileDelFlagEnums.DEL.getFlag());
//          批量删除
            fileInfoMapper.updateFileDelFlagBatch(updateInfo, userId, delFilePidList, null, FileDelFlagEnums.USING.getFlag());
        }

//        将所选文件或者所需目录更新为回收站
        List<String> delFileIdList = Arrays.asList(fileIdArray);
        FileInfo fileInfo = new FileInfo();
        fileInfo.setRecoveryTime(new Date());
        fileInfo.setDelFlag(FileDelFlagEnums.RECYCLE.getFlag());
        this.fileInfoMapper.updateFileDelFlagBatch(fileInfo, userId, null, delFileIdList, FileDelFlagEnums.USING.getFlag());

    }

    /**
     * 回收站文件恢复
     * @param userId
     * @param fileIds
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void recoverFileBatch(String userId, String fileIds) {
        String[] fileIdArray = fileIds.split(",");
        FileInfoQuery fileInfoQuery = new FileInfoQuery();
        fileInfoQuery.setUserId(userId);
        fileInfoQuery.setFileIdArray(fileIdArray);
        fileInfoQuery.setDelFlag(FileDelFlagEnums.RECYCLE.getFlag());
        List<FileInfo> fileInfos = fileInfoMapper.selectList(fileInfoQuery);
        ArrayList<String> delFileSubFloderFileList = new ArrayList<>();
//        将所选目录下的所有文件夹都存在delFileSubFloderFileList里面
        for (FileInfo fileInfo : fileInfos){
            if(FileFolderTypeEnums.FOLDER.getType().equals(fileInfo.getFolderType())){
                findAllSubFolderFileList(fileInfo.getFileId(),userId,delFileSubFloderFileList,FileDelFlagEnums.DEL.getFlag());
            }
        }
//      查询根目录下的所有文件
        fileInfoQuery =new FileInfoQuery();
        fileInfoQuery.setUserId(userId);
        fileInfoQuery.setDelFlag(FileDelFlagEnums.USING.getFlag());
        fileInfoQuery.setFilePid(Constants.ZERO_STR);
        List<FileInfo> allRootFileList = this.fileInfoMapper.selectList(fileInfoQuery);
        Map<String, FileInfo> rootFileMap = allRootFileList
                .stream()
                .collect(
                        Collectors
                                .toMap(FileInfo::getFileName, Function.identity(),
                                        (file1, file2) -> file2));
//        将查询的所有文件夹更新为使用中
        if(!delFileSubFloderFileList.isEmpty()){
            FileInfo updateInfo = new FileInfo();
            updateInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());
            fileInfoMapper.updateFileDelFlagBatch(updateInfo, userId, delFileSubFloderFileList,null,  FileDelFlagEnums.DEL.getFlag());
        }

//        将所选文件更新为正常，且跟到根目录里面
        List<String> delFileIdList = Arrays.asList(fileIdArray);
        FileInfo fileInfo = new FileInfo();
        fileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());
        fileInfo.setFilePid(Constants.ZERO_STR);
        fileInfo.setLastUpdateTime(new Date());
        this.fileInfoMapper.updateFileDelFlagBatch(fileInfo, userId, null, delFileIdList, FileDelFlagEnums.RECYCLE.getFlag());

        //将所选文件重命名
        for (FileInfo item : fileInfos) {
            FileInfo rootFileInfo = rootFileMap.get(item.getFileName());
            //文件名已经存在，重命名被还原的文件名
            if (rootFileInfo != null) {
                String fileName = StringTools.rename(item.getFileName());
                FileInfo updateInfo = new FileInfo();
                updateInfo.setFileName(fileName);
                fileInfoMapper.updateByFileIdAndUserIdFileInfo(updateInfo, item.getFileId(), userId);
            }
        }

    }

//    TODO 突出点：使用事物的范围，还有问题：例如删除了，但没有更新用户空间
    @Override
    public void delFileBatch(String userId, String fileIds, Boolean adminOp) {
//        删除已经选择的事物,分为两个事物，主要是因为更新用户空间会受到未删除（事物未完成之前数据）的影响，所以需要两个事物
        deleteFilesInTransaction(userId, fileIds, adminOp);
//        更新删除之后用户的使用空间
        updateUserSpaceInTransaction(userId);
    }

    /**
     * 彻底删除所选文件
     * @param userId
     * @param fileIds
     * @param adminOp
     */
    @Transactional
    protected void deleteFilesInTransaction(String userId, String fileIds, Boolean adminOp) {
        // 删除文件的逻辑
        String[] fileIdArray = fileIds.split(",");
        FileInfoQuery fileInfoQuery = new FileInfoQuery();
        fileInfoQuery.setUserId(userId);
        fileInfoQuery.setFileIdArray(fileIdArray);
        fileInfoQuery.setDelFlag(FileDelFlagEnums.RECYCLE.getFlag());
        List<FileInfo> fileInfos = fileInfoMapper.selectList(fileInfoQuery);
//        该目标文件下所有的文件夹
        ArrayList<String> delFileSubFloderFileList = new ArrayList<>();
        for (FileInfo fileInfo : fileInfos){
            if(FileFolderTypeEnums.FOLDER.getType().equals(fileInfo.getFolderType())){
                findAllSubFolderFileList(fileInfo.getFileId(),userId,delFileSubFloderFileList,FileDelFlagEnums.DEL.getFlag());
            }
        }

//        删除所选文件子目录中的文件
        if(!delFileSubFloderFileList.isEmpty()){
            fileInfoMapper.delFileBatch(userId,delFileSubFloderFileList,null,adminOp ? null : FileDelFlagEnums.DEL.getFlag());
        }
//        删除所选文件或文件夹
        fileInfoMapper.delFileBatch(userId,null,Arrays.asList(fileIdArray),adminOp ? null : FileDelFlagEnums.RECYCLE.getFlag());
    }

    /**
     * 删除所选之后，更新用户空间
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    protected void updateUserSpaceInTransaction(String userId) {
        // 更新用户使用空间, 更新缓存, 获取被删除文件之后，用户文件的大小
        Long useSpace = fileInfoMapper.selectUseSpaceByUserId(userId);

        UserInfo userInfo = new UserInfo();
        userInfo.setUseSpace(useSpace);
        int i = fileInfoMapper.updateByUserId(userInfo, userId);

        // 设置缓存, 更新用户缓存使用空间
        UserSpaceDto userSpaceUser = (UserSpaceDto) redisComponent.getUserSpaceUser(userId);
        userSpaceUser.setUseSpace(useSpace);
        redisComponent.saveUserSpaceUser(userId, userSpaceUser);
    }

    @Override
    public FileInfo getFileInfoByFileIdAndUserId(String fileId, String userId) {
        FileInfo fileInfo = fileInfoMapper.selectByFileIdAndUserIdFileInfo(fileId, userId);
        return fileInfo;
    }

    @Override
    public void checkRootFilePid(String rootFilePid, String userId, String fileId) {
        if (StringTools.isEmpty(fileId)) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        if (rootFilePid.equals(fileId)) {
            return;
        }
        checkFilePid(rootFilePid, fileId, userId);
    }

    private void checkFilePid(String rootFilePid, String fileId, String userId) {
        FileInfo fileInfo = fileInfoMapper.selectByFileIdAndUserIdFileInfo(fileId, userId);
        if (fileInfo == null) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        if (Constants.ZERO_STR.equals(fileInfo.getFilePid())) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        if (fileInfo.getFilePid().equals(rootFilePid)) {
            return;
        }
        checkFilePid(rootFilePid, fileInfo.getFilePid(), userId);
    }

    private void  findAllSubFolderFileList(String fileId, String userId, List<String> fileList,Integer delFlag){
        fileList.add(fileId);
        FileInfoQuery fileInfoQuery = new FileInfoQuery();
        fileInfoQuery.setUserId(userId);
        fileInfoQuery.setFilePid(fileId);
        fileInfoQuery.setDelFlag(delFlag);
        fileInfoQuery.setFolderType(FileFolderTypeEnums.FOLDER.getType());
        List<FileInfo> fileInfos = fileInfoMapper.selectList(fileInfoQuery);
        for (FileInfo fileInfo : fileInfos) {
            findAllSubFolderFileList(fileInfo.getFileId(),userId,fileList,delFlag);
        }
    }


    /**
     * 校验文件名是否存在
     */
    private void checkFileName(String filePid, String userId, String fileName,Integer folderType) {
        FileInfoQuery fileInfoQuery = new FileInfoQuery();
        fileInfoQuery.setUserId(userId);
        fileInfoQuery.setFilePid(filePid);
        fileInfoQuery.setFileName(fileName);
        fileInfoQuery.setFolderType(folderType);
        fileInfoQuery.setDelFlag(FileDelFlagEnums.USING.getFlag());
        Integer count = this.fileInfoMapper.selectCount(fileInfoQuery);
        if (count > 0) {
           throw new BusinessException("此目录文件名已存在,请更换名称!");
        }
    }


    /**
     * 文件重命名：
     * 如果文件名存在就重命名，不存在就直接返回原名
     * @param filePid
     * @param userId
     * @param fileName
     * @return
     */
    private String autoRename(String filePid, String userId, String fileName) {
        FileInfoQuery fileInfoQuery = new FileInfoQuery();
        fileInfoQuery.setUserId(userId);
        fileInfoQuery.setFilePid(filePid);
        fileInfoQuery.setDelFlag(FileDelFlagEnums.USING.getFlag());
        fileInfoQuery.setFileName(fileName);
        Integer count = this.fileInfoMapper.selectCount(fileInfoQuery);
        if (count > 0) {
            return StringTools.rename(fileName);
        }

        return fileName;
    }

    /**
     * 用户文件空间更新：
     * @return
     */
    private void updateUserSpace(SessionWebUserDto webUserDto, Long totalSize) {
        Integer count = userInfoMapper.updateUserSpace(webUserDto.getUserId(), totalSize, null);
        if (count == 0) {
            throw new BusinessException(ResponseCodeEnum.CODE_904);
        }
        UserSpaceDto spaceDto = (UserSpaceDto) redisComponent.getUserSpaceUser(webUserDto.getUserId());
        spaceDto.setUseSpace(spaceDto.getUseSpace() + totalSize);
        redisComponent.saveUserSpaceUser(webUserDto.getUserId(), spaceDto);
    }

    /**
     * 保持分享的文件到自己登录的网盘中
     * @param shareRootFilePid 根文件的父目录id
     * @param shareFileIds 分享的文件id
     * @param myFolderId 保持分享的文件到自己文件夹中
     * @param shareUserId 分享人
     * @param cureentUserId 当前用户
     */
    @Override
    @Transactional
    public void saveShare(String shareRootFilePid, String shareFileIds, String myFolderId, String shareUserId, String cureentUserId) {
        String[] shareFileIdArray = shareFileIds.split(",");
        //目标目录文件列表
        FileInfoQuery fileInfoQuery = new FileInfoQuery();
        fileInfoQuery.setUserId(cureentUserId);
        fileInfoQuery.setFilePid(myFolderId);
        List<FileInfo> currentFileList = this.fileInfoMapper.selectList(fileInfoQuery);
        Map<String, FileInfo> currentFileMap = currentFileList.stream().collect(Collectors.toMap(FileInfo::getFileName, Function.identity(), (file1, file2) -> file2));
        //选择的文件
        fileInfoQuery = new FileInfoQuery();
        fileInfoQuery.setUserId(shareUserId);
        fileInfoQuery.setFileIdArray(shareFileIdArray);
        List<FileInfo> shareFileList = this.fileInfoMapper.selectList(fileInfoQuery);
        //重命名选择的文件
        List<FileInfo> copyFileList = new ArrayList<>();
        Date curDate = new Date();
        for (FileInfo item : shareFileList) {
            FileInfo haveFile = currentFileMap.get(item.getFileName());
            if (haveFile != null) {
                item.setFileName(StringTools.rename(item.getFileName()));
            }
            findAllSubFile(copyFileList, item, shareUserId, cureentUserId, curDate, myFolderId);
        }
        fileInfoMapper.insertBatch(copyFileList);

        //更新空间
        Long useSpace = this.fileInfoMapper.selectUseSpaceByUserId(cureentUserId);
        UserInfo dbUserInfo = this.userInfoMapper.selectByUserId(cureentUserId);
        if (useSpace > dbUserInfo.getTotalSpace()) {
            throw new BusinessException(ResponseCodeEnum.CODE_904);
        }
        UserInfo userInfo = new UserInfo();
        userInfo.setUseSpace(useSpace);
        this.userInfoMapper.updateByUserId(userInfo, cureentUserId);
        //设置缓存
        UserSpaceDto userSpaceDto = (UserSpaceDto) redisComponent.getUserSpaceUser(cureentUserId);
        userSpaceDto.setUseSpace(useSpace);
        redisComponent.saveUserSpaceUser(cureentUserId, userSpaceDto);
    }

    private void findAllSubFile(List<FileInfo> copyFileList, FileInfo fileInfo, String sourceUserId, String currentUserId, Date curDate, String newFilePid) {
        String sourceFileId = fileInfo.getFileId();
        fileInfo.setCreateTime(curDate);
        fileInfo.setLastUpdateTime(curDate);
        fileInfo.setFilePid(newFilePid);
        fileInfo.setUserId(currentUserId);
        String newFileId = StringTools.getRandomString(Constants.LENGTH_10);
        fileInfo.setFileId(newFileId);
        copyFileList.add(fileInfo);
        if (FileFolderTypeEnums.FOLDER.getType().equals(fileInfo.getFolderType())) {
            FileInfoQuery query = new FileInfoQuery();
            query.setFilePid(sourceFileId);
            query.setUserId(sourceUserId);
            List<FileInfo> sourceFileList = this.fileInfoMapper.selectList(query);
            for (FileInfo item : sourceFileList) {
                findAllSubFile(copyFileList, item, sourceUserId, currentUserId, curDate, newFileId);
            }
        }
    }



}
